#pragma once

#include "common.h"
#include <vector>
#include "Random.h"

template<int size = 2, int unitsize = 8>
class LfsrRegister : 
	public integer_datatype_selector<CELLS_NEEDED(BYTE, size, unitsize)>,
	public ArrayPacker<BYTE, unitsize, size>
{
public:
	typedef integer_datatype RegisterData;
	static const int dataSize = size;
	LfsrRegister(RegisterData v = 0)
	{
		STATIC_CHECK(sizeof(RegisterData) >= size, register_size_too_large);
		setMask();
		_data.i = v & _mask;
	}

	LfsrRegister(const std::vector<BYTE> &v)
	{
		STATIC_CHECK(sizeof(RegisterData) >= size, register_size_too_large);
		assert(v.size() >= size);
		pak<BYTE>(v, _data.b);
		setMask();
	}

	LfsrRegister(const LfsrRegister &other)
	{
		setMask();
		initFrom(other);
	}

	LfsrRegister& operator = (const LfsrRegister &other)
	{
		if(this != &other)
		{
			initFrom(other);
		}
		return *this;
	}

	BYTE getElement(int index) const
	{ 
		return get(_data.b, index);
	}

	void setElement(int index, BYTE value)
	{ 
		set(_data.b, index, value);
	}

	typename RegisterData data() const
	{
		return _data.i;
	}

	void rightShift()
	{
		_data.i = _data.i >> unitsize;
	}

	void leftShift()
	{
		_data.i = (_data.i << unitsize) & _mask;
	}
	void increment()
	{
		_data.i = (_data.i + 1) & _mask;
	}
	bool operator == (const LfsrRegister &other) const
	{
		return _data.i == other._data.i;
	}
	bool operator < (const LfsrRegister &other) const
	{
		return _data.i < other._data.i;
	}

	bool operator != (const LfsrRegister &other) const
	{
		return !operator ==(other);
	}

	operator typename RegisterData ()
	{
		return _data.i;
	}

	operator const char *()
	{
		return (char *)_data.b;
	}

	std::string toStringAsRow() const
	{
		std::string res = "[";
		for(int i = 0; i < size; i++)
		{
			char buf[10];
			sprintf(buf, " %d ", getElement(i));
			res.append(buf);
		}
		res.append("]");
		return res;
	}

	void fillAtRandom(Random *random)
	{
		_data.i = 0;
		for (int i = 0; i < size / 2; i++)
		{
			_data.i |= (QWORD)(random->next() << i * 16);
		}
	}

private:
	union Register
	{	
		BYTE b[size];
		typename RegisterData i;
	};
	typename RegisterData _mask;
	Register _data;

	void setMask()
	{
		//_mask = ((~typename RegisterData(0)) >> 
		//		((sizeof(typename RegisterData) - size)*unitsize));
		_mask = MASK(typename RegisterData, size * unitsize);
	}

	void initFrom(const LfsrRegister &other)
	{
		_data.i = other._data.i;
	}

};

template <int regSize = 2>
class LfsrRegisterComparer
{
public:
	bool operator () (
		const LfsrRegister<regSize> &a, 
		const LfsrRegister<regSize> &b) const
	{
		return a < b;
	}
};